;################ ATmega8 Binary Clock Program ##################
;################ By Simon van Veen            ##################

; Connect 6 leds to PC0-PC5 to indicate minutes
; Connect 5 leds to PB1-PB5 to indicate hours
; Connect 1 led to PB0 to indicate seconds counting


;---------------- DEFENITIONS -----------------------------------
.include "m8def.inc"
.def seconds = r20
.def minutes = r21
.def hours   = R22

.def almin   = R23
.def alhours = R24


;---------------- INITIALISATION --------------------------------
.org 0
	rjmp  RESET				; Reset Handler
	rjmp  EXT_INT0			; external interrupt 
	rjmp  EXT_INT1

.org OVF2addr				; OVF2addr = $004
	rjmp  TIM2_OVF 			; Timer2 Overflow Handler

RESET:
	ldi   r16, high(RAMEND)	; Main program start
	out   SPH,r16			; Set Stack Pointer to top of RAM
	ldi   r16, low(RAMEND)
	out   SPL, r16
	ldi   r16, (1<<AS2)		; Asynchronous Timer 2
	out   ASSR, r16			
	ldi   r16, (1<<TOIE2)	; Timer 2 Overflow Interrupt Enable
	out   TIMSK, r16 		
	ldi   r16, (1<<SE)		; Sleep Enable
	out   MCUCR, r16
    ldi   r16, 0b00000101  	; Prescale CK/128  (101)
    out   TCCR2, r16
	ldi   r16, (1<<INT0)    ; External interrupt enable
	out   GICR, r16	
;	ldi   r16, (1<<ISC01)   ;  edge generates interrupt
;	out   MCUCR, r16
	ser   r16               ; Input on Port D
	out   PORTD, r16

PRESET:			         	; This is temporary to set the time
	ldi   hours, 22         ; 
	ldi   minutes, 51
	ldi   seconds, 30
	lsl   hours             

	ldi   alhours,22			; Temporay set alarm
	ldi   almin,52
	lsl   alhours
	rjmp  WAIT

RESETTIME:
	clr	  minutes
	clr   seconds
	clr   hours
	rjmp  WAIT

;--------------- COUNTING TIME ------------------------------
INCHOUR:					; Minutes has reached 60, so an hour has to be added
	clr   minutes
	inc   hours
	inc   hours				; Hours + 2, because we don't use port 0
	rjmp  WAIT

INCMIN:						; Seconds has reached 60, so a minute has to be added
	clr   seconds
	inc   minutes
	rjmp  WAIT

WAIT:
	sei						; Global interrupt enable
	cp    minutes, almin    ; Check if alarm has to go off (minutes)
	breq  CPHOURS
	sleep

CPHOURS:					; Check if the alarm has to go off (hours)
	cp    hours, alhours
	breq  ALARM
	breq  CPSECONDS
	sleep

CPSECONDS:                  ; The alarm buzzes every second so check if seconds ends with a 0
	mov   r16, seconds
	cbr   r16, 0b11111110
	cpi   r16, 0b00000001
	breq  ALARM
	sleep

;---------------- TIMER2 OVERFLOW ------------------------------
TIM2_OVF:
	out   DDRC, minutes
	out   PORTC, minutes 		; Port D high

DISPLAY:
	mov   r16, seconds
	com   r16
	cbr   r16, 0b11111110       ; Show blinking seconds led
	add   r16, hours

	out   DDRB, r16
	out   PORTB, r16

	inc   seconds
	cpi   seconds, 60		; Compare seconds with 60
	breq  INCMIN
	cpi   minutes, 60		; Compare minutes with 60
	breq  INCHOUR

	cpi   hours, 48
	breq  RESETTIME
	rjmp  WAIT


;---------------- BUZZ ALARM      ------------------------------
ALARM:                      ; A timer overflow interrupt can break this loop
	ser    r16
	out    DDRD, r16
	ldi    r16, 0b00000010 	; Piezo ports + - 
	out    PORTD, r16
	rcall  DELAY

	ldi    r16, 0b00000001	; Piezo ports - +
	out    PORTD, r16

	rcall  DELAY
	rjmp   ALARM            ; Loop back

DELAY:
	.set  d1 = 2           ; Adjustable frequency 
	.set  d2 = 250
	ldi    R30, d1
DELAYLOOP1:
	ldi    R31, d2
DELAYLOOP2:
	dec    R31	
	nop	
	brne   DELAYLOOP2	
	dec    R30	
	cpi    R30,0
	brne   DELAYLOOP1	
	ret

;------------------- BUTTON INTERRUPTS --------------------
EXT_INT0:                   ; Low level on int0 generates interrupt
	in   r16, PORTD  		; Read Pins

	mov  r17, r16
	cbr  r17, 0b10111111    ; Only keep pin 6
	ldi  r18, 0b01000000
	cpse r17, r18           ; Button pressed
	inc  hours

	cpi  r16, 0b00000100
	brlo EXT_INT0			; Branch back if pin 2 is pressed (bit 2 = 0)
	rjmp WAIT
EXT_INT1:
	rjmp WAIT

